/**
 * @project: parallel-task
 * @package: com.ngplat.paralleltask.utils
 * @filename: XmlUtils.java
 *
 * Copyright (c) 2018 eSunny Info. Tech Ltd. All rights reserved.
 * 
 */
package com.ngplat.paralleltask.utils;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ngplat.paralleltask.model.TaskInfo;

/**
 * @typename: XmlUtils
 * @brief: XML文件解析工具类
 * @author: KI ZCQ
 * @date: 2018年5月30日 下午8:34:50
 * @version: 1.0.0
 * @since
 * 
 */
public class XmlUtils {

	private final static Logger logger = LoggerFactory.getLogger(XmlUtils.class);

	/**
	 * @param <T>
	 * @Description: DMO4J写入XML
	 * @param obj
	 *            泛型对象
	 * @param entityPropertys
	 *            泛型对象的List集合
	 * @param Encode
	 *            XML自定义编码类型(推荐使用GBK)
	 * @param xmlFilePath
	 *            XML文件的路径及文件名
	 */
	public static <T> void writeXmlDocument(T obj, List<T> entityPropertys, String Encode, String xmlFilePath) {
		long lasting = System.currentTimeMillis();// 效率检测

		try {
			// 声明写XML的对象
			XMLWriter writer = null;
			OutputFormat format = OutputFormat.createPrettyPrint();
			// 设置XML文件的编码格式
			format.setEncoding(Encode);
			// 获得文件地址
			String filePath = xmlFilePath;
			// 获得文件
			File file = new File(filePath);

			if (file.exists()) {
				file.delete();
			}
			// 新建student.xml文件并新增内容
			Document document = DocumentHelper.createDocument();
			// 获得类名
			String rootname = obj.getClass().getSimpleName();
			// 添加根节点
			Element root = document.addElement(rootname + "s");
			// 获得实体类的所有属性
			Field[] properties = obj.getClass().getDeclaredFields();

			// 递归实体
			for (T t : entityPropertys) {
				// 二级节点
				Element secondRoot = root.addElement(rootname);

				for (int i = 0; i < properties.length; i++) {
					// 反射get方法
					Method meth = t.getClass().getMethod("get" + properties[i].getName().substring(0, 1).toUpperCase()
							+ properties[i].getName().substring(1));
					// 为二级节点添加属性，属性值为对应属性的值
					secondRoot.addElement(properties[i].getName()).setText(meth.invoke(t).toString());

				}
			}
			// 生成XML文件
			writer = new XMLWriter(new FileWriter(file), format);
			writer.write(document);
			writer.close();
			long lasting2 = System.currentTimeMillis();
			logger.info("写入XML文件-{}-结束,用时 {} ms", xmlFilePath, (lasting2 - lasting));
		} catch (Exception e) {
			logger.error("XML文件-{}-写入失败, 请检查, {}", xmlFilePath, e);
		}
	}

	/**
	 * @Description: 实现读取XML文件得到对象集合的方法
	 * @param xmlFilePath
	 *            XML文件的路径和地址
	 * @param <T>
	 *            泛型对象
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static <T> List<T> readXML(String xmlFilePath, Class<T> tClass) {
		// 效率检测
		long lasting = System.currentTimeMillis();
		// 创建list集合
		List<T> list = new ArrayList<T>();
		try {
			// 读取文件
			File f = new File(xmlFilePath);
			// dom4j读取
			SAXReader reader = new SAXReader();
			Document doc = reader.read(f);
			// 获得根节点
			Element root = doc.getRootElement();
			// 二级节点
			Element foo;
			// 获得实例的属性
			Field[] properties = tClass.getDeclaredFields();
			// 实例的set方法
			Method setmeth;

			// 遍历t.getClass().getSimpleName()节点
			for (Iterator i = root.elementIterator(); i.hasNext();) {
				// 下一个二级节点
				foo = (Element) i.next();
				// 获得对象的新的实例
				T t = (T) tClass.newInstance();
				// 遍历所有孙子节点
				for (int j = 0; j < properties.length; j++) {
					// 实例的set方法
					setmeth = tClass.getMethod("set" + properties[j].getName().substring(0, 1).toUpperCase()
							+ properties[j].getName().substring(1), properties[j].getType());
					// properties[j].getType()为set方法入口参数的参数类型(Class类型)
					setmeth.invoke(t, foo.elementText(properties[j].getName()));// 将对应节点的值存入
				}
				// 添加结果集
				list.add(t);
			}
		} catch (Exception e) {
			logger.error("读取XML文件-{}-出现异常, 请检查: {}", xmlFilePath, e);
			return new ArrayList<T>();
		}
		long lasting2 = System.currentTimeMillis();
		logger.info("读取XML文件-{}-结束, 用时 {} ms", xmlFilePath, (lasting2 - lasting));
		return list;
	}

	/**
	 * @Description: 从指定节点开始,递归遍历所有子节点
	 * @param node
	 *            指定节点
	 * @param recursive true - 递归子节点, false - 不递归
	 */
	@SuppressWarnings("unchecked")
	public static void getNodes(Element node, boolean recursive) {
		// 当前节点的名称、文本内容和属性
		logger.info("------开始解析节点{}, 当前节点名称：{}, 当前节点的内容：{}------", node.getName(), node.getName(), node.getTextTrim());
		// 当前节点的所有属性的list
		List<Attribute> listAttr = node.attributes();
		// 遍历当前节点的所有属性
		for (Attribute attr : listAttr) {
			// 属性名称
			String name = attr.getName();
			// 属性的值
			String value = attr.getValue();
			System.out.println("属性名称：" + name + "属性值：" + value);
		}

		// 是否递归
		if(recursive) {
			// 递归遍历当前节点所有的子节点
			List<Element> listElement = node.elements();
			// 遍历所有一级子节点
			for (Element e : listElement) {
				// 递归
				XmlUtils.getNodes(e, recursive);
			}
		}
	}

	public static void main(String[] args) throws DocumentException {

		// xml path
		String path = XmlUtils.class.getClassLoader().getResource("xml/task_xml_template.xml").getPath();
		
		// 
		XmlUtils.readXML(path, TaskInfo.class);
		
		// 创建一个SAXReader对象
		SAXReader sax = new SAXReader();
		// 根据指定的路径创建file对象
		File xmlFile = new File(path);
		// 获取document对象,如果文档无节点，则会抛出Exception提前结束
		Document document = sax.read(xmlFile);
		// 获取根节点
		Element root = document.getRootElement();
		// 从根节点开始遍历所有节点
		XmlUtils.getNodes(root, true);
	}

}
